#pragma once
#include <iostream>
#include <fstream>
#include "LockGuard.hpp"
#include <stdarg.h>
// OS
#include <unistd.h>
#include <sys/types.h>

#define LogToScreen 1
#define LogToFile 3
const char *glogfile = "log.txt";

enum
{
    DEBUG = 1,
    INFO,
    WARNING,
    ERROR,
    FATAL
};
std::string LevelToString(int level)
{
    switch (level)
    {
    case DEBUG:
        return "DEBUG";
    case INFO:
        return "INFO";
    case WARNING:
        return "WARNING";
    case ERROR:
        return "ERROR";
    case FATAL:
        return "FATAL";
    default:
        return "UNKNOW";
    }
}

struct LogMessage // 日志信息
{
    std::string GetLogMessage()
    {
        char logtxt[1024] = {0};
        snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%s][%d] %s",
                 _filename.c_str(), _filenumber, _level.c_str(), _cur_time.c_str(), _pid, _info.c_str());
        return logtxt; // 隐式类型转换
    }
    std::string _filename;
    int _filenumber;
    std::string _level;
    std::string _cur_time;
    pid_t _pid;
    std::string _info;
};
class Log
{
public:
    // 法一：
    //  Log(std::string filename, int filenumber, int level, std::string info, int type, std::string logfile = "log.txt")
    //      : _type(type), _logfile(logfile)
    // 法二：
    // 缺省参数必须从后往前写
    //  Log(int level, std::string info, int type, std::string filename = __FILE__, int filenumber = __LINE__, std::string logfile = "log.txt")
    //      : _type(type), _logfile(logfile)
    // 法三： //可变参数版写法
    Log(int type, const std::string logfile = glogfile)
        :_type(type) , _logfile(logfile)
    {
        pthread_mutex_init(&_mtx, nullptr);

        // 固定参数时的写法
        //// 构造/填充日志信息
        // lg._filename = filename;
        // lg._filenumber = filenumber;
        // lg._level = LevelToString(level);
        // lg._cur_time = GetCurTime();
        // lg._pid = getpid();
        // lg._info = info;

        //FlushMessage();
    }
    void Enable(int type) // 输出的方向
    {
        _type = type;
    }
    void FlushToScreen()
    {
        // 法一：
        //  printf("[%s][%d][%s][%s][%d] %s",
        //  lg._filename, lg._filenumber, lg._level, lg._cur_time, lg._pid, lg._message_info);
        // 法二：
        std::string logtxt = lg.GetLogMessage();
        std::cout << logtxt << std::endl;
    }
    void FlushToFile()
    {
        // 过滤器---把同类型/同等级的log放在同一个文件
        if (lg._level == LevelToString(DEBUG))
            _logfile = "debug.txt";
        else if (lg._level == LevelToString(INFO))
            _logfile = "info.txt";
        else if (lg._level == LevelToString(WARNING))
            _logfile = "warning.txt";
        else if (lg._level == LevelToString(ERROR))
            _logfile = "error.txt";
        else if (lg._level == LevelToString(FATAL))
            _logfile = "fatal.txt";

        std::ofstream out(_logfile, std::ios::app); // 以追加打开文件
        if (!out.is_open())
        {
            return;
        }
        // 法一：
        // char logtxt[1024] = {0};
        // snprintf(logtxt, sizeof(logtxt), "[%s][%d][%s][%s][%d] %s",
        // lg._filename, lg._filenumber, lg._level, lg._cur_time, lg._pid, lg._message_info);
        // 法二：
        std::string logtxt = lg.GetLogMessage();
        out.write(logtxt.c_str(), logtxt.size());
        out.close();
    }
    void FlushMessage()
    {
        LockGuard lock(&_mtx); // 加锁
        if (_type == LogToScreen)
            FlushToScreen();
        else //(_type == LogToFile)
            FlushToFile();
    }

    std::string GetCurTime()
    {
        time_t now = time(nullptr);
        struct tm *t = localtime(&now);
        char time[1024] = {0};
        snprintf(time, sizeof(time), "%04d-%02d-%02d_%02d:%2d:%2d",
                 t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
        return time;
    }

    // 可变参数版写法
    void LogMessage(std::string filename, int filenumber, int level, const char *format, ...)
    {
        // 构造/填充日志信息
        lg._filename = filename;
        lg._filenumber = filenumber;
        lg._level = LevelToString(level);
        lg._cur_time = GetCurTime();
        lg._pid = getpid();
        // 获取可变参数列表
        va_list vl;
        va_start(vl, format);
        char log_info[1024] = {0};
        vsnprintf(log_info, sizeof(log_info), format, vl); // 把format和可变参数组成字符串
        lg._info = log_info;

        // 刷新，打印
        FlushMessage();
    }

    ~Log()
    {
        pthread_mutex_destroy(&_mtx);
    }

    ::LogMessage lg; // 使用外面/全局的哪个标识符
private:
    int _type;
    std::string _logfile;
    pthread_mutex_t _mtx;
};

Log log_info(LogToScreen);

#define LOG(level, format, ...) do {     \
        log_info.LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); \
        } while (0)

#define LOG_TO_SCREEN   do {Enable(LogToScreen);}while(0)
#define LOG_TO_FILE   do {Enable(LogToFile);}while(0)